use std::io;
use std::time::Duration;
use pretty_hex::PrettyHex;
use tokio::io::AsyncWriteExt;
use tokio::sync::mpsc;
use crate::configs::AgentConf;
use crate::error::Error;
use tokio::time;
use crate::cloud_pkt::{CloudPacket, CPPayload, ProtoCloudHeader, RTDataUploadReqPayload};
use crate::data_upload::defs::UploadDataType;
use crate::data_upload::upload_pkt::{AlarmDataFrame, DriveMotorDataFrame, DriveMotorDataFrameItem, EngineDataFrame, ExtremeValueDataFrame, FuelCellDataFrame, LocationDataFrame, UploadPayloadItem, UploadPayloadItemFrame, UserCustomDataFrame, VehicleDataFrame};
use crate::defs::{CloudCmdType, EncryptionType, ResponseType};
use crate::pkt_gen::gen_heartbeat_req;
use crate::upstream::agent::Agent;
use crate::util::fill_current_time;
use tokio::sync::mpsc::{Sender};
use crate::data_upload::upload_pkt::UploadPayloadItemFrame::UserCustom;
use crate::upstream::cacher::{CacheType, MsgCache};

///
/// 会话loop 处理
///
pub async fn loop_process(agent_ins: &mut Agent, agent_conf: &AgentConf) -> Result<(), Error> {
    // Step: 上报数据
    let mut up_interval = time::interval(Duration::from_secs(agent_conf.upload_rt_interval as u64));
    let mut hb_interval = time::interval(Duration::from_secs(agent_conf.heartbeat_interval as u64));
    let client = agent_ins.client.as_mut().unwrap();
    let (cache_msg_hb_tx, mut cache_msg_hb_rx) = mpsc::channel::<CloudPacket>(5);

    let mut send_fail_cnt: u16 = 0;
    let mut tcp_recv_msg = vec![0; 1024];
    // 测试 - 实时数据发送计数, 每3个丢重传一次
    let mut test_rt_cnt = 0;
    let mut test_rt_to_retry_limit: u8 = 3;

    // 测试 - 重传数据发送计数, 每2个重传失败一次
    let mut test_retry_cnt = 0;
    let mut test_retry_to_reissue_limit: u8 = 2;

    loop {
        tokio::select! {
            _ = up_interval.tick() => {
                let p_pkt = upload_data_rt(agent_conf).await;
                let ret = agent_ins.msg_cache.write_pkt(CacheType::Realtime, p_pkt).await;
                if ret.is_err() {
                    // TODO: 写入失败应该处理
                    log::error!("Write data to realtime channel failed -> {:?}", ret.unwrap_err());
                }
                log::info!("Realtime tick reached");
            }
            _ = hb_interval.tick() => {
                send_heartbeat(cache_msg_hb_tx.clone(), agent_conf).await;
            }
            Some(mut rt_pkt) = agent_ins.msg_cache.msg_rt_rx.recv() => {
                // realtime channel接收到了数据
                if test_rt_cnt >= test_rt_to_retry_limit {
                    // 测试,模拟发送失败
                    // TODO: 应该先加一分钟的定时器,然后定时器触发后加入队列
                    let retry_time = agent_conf.retry_sleep_time as u64;
                    send_data_to_retry(retry_time,  &mut agent_ins.msg_cache, rt_pkt).await;
                    test_rt_cnt = 0;
                    continue;
                }
                let pkt_bytes = rt_pkt.to_bytes();
                log::info!("Realtime data {:02x} payload dump: {:?}", test_rt_cnt, pkt_bytes.hex_dump());
                let wret = client.write_all(pkt_bytes.as_ref()).await;
                log::info!("Send realtime data write return {:?}", wret);
                if wret.is_err() {
                    // 写入失败,无论任何原因,该pkt进入retry队列
                    // TODO: 应该先加一分钟的定时器,然后定时器触发后加入队列
                    let retry_time = agent_conf.retry_sleep_time as u64;
                    send_data_to_retry(retry_time,  &mut agent_ins.msg_cache, rt_pkt).await;
                    continue;
                }
                let wret = client.flush().await;
                log::info!("Send realtime data flush return {:?}", wret);
                if wret.is_err() {
                    // TODO: 应该先加一分钟的定时器,然后定时器触发后加入队列
                    let retry_time = agent_conf.retry_sleep_time as u64;
                    send_data_to_retry(retry_time,  &mut agent_ins.msg_cache, rt_pkt).await;
                }
                test_rt_cnt = test_rt_cnt.wrapping_add(1);
            }
            Some(mut retry_pkt) = agent_ins.msg_cache.msg_retry_rx.recv() => {
                // retry channel接收到了数据
                if test_retry_cnt >= test_retry_to_reissue_limit {
                    // 测试用,用于模拟重传丢包
                    retry_pkt.retry_cnt = retry_pkt.retry_cnt + 1;
                    let retry_time = agent_conf.retry_sleep_time as u64;
                    send_data_to_retry(retry_time,  &mut agent_ins.msg_cache, retry_pkt).await;
                    test_retry_cnt = 0;
                    continue;
                }
                test_retry_cnt = test_retry_cnt.wrapping_add(1);
                let pkt_bytes = retry_pkt.to_bytes();
                log::info!("Retry data payload dump:\n{:?}", pkt_bytes.hex_dump());
                let wret = client.write_all(pkt_bytes.as_ref()).await;
                log::info!("Send retry data write return {:?}", wret);
                if wret.is_err() {
                    // 写入失败,无论任何原因,该pkt进入retry队列
                    retry_pkt.retry_cnt = retry_pkt.retry_cnt + 1;
                    let retry_time = agent_conf.retry_sleep_time as u64;
                    send_data_to_retry(retry_time,  &mut agent_ins.msg_cache, retry_pkt).await;
                    continue;
                }
                let wret = client.flush().await;
                log::info!("Send retry data flush return {:?}", wret);
                if wret.is_err() {
                    retry_pkt.retry_cnt = retry_pkt.retry_cnt + 1;
                    let retry_time = agent_conf.retry_sleep_time as u64;
                    send_data_to_retry(retry_time,  &mut agent_ins.msg_cache, retry_pkt).await;
                }
            }
            Some(mut hb_pkt) = cache_msg_hb_rx.recv() => {
                // 心跳channel接收到了数据
                let pkt_bytes = hb_pkt.to_bytes();
                log::info!("Heartbeat payload dump: {:?}", pkt_bytes.hex_dump());
                let wret = client.write_all(pkt_bytes.as_ref()).await;
                log::debug!("Send heartbeat data write return {:?}", wret);
                if wret.is_err() {
                    // 发送心跳失败
                    // send_fail_cnt = send_fail_cnt + 1;
                    send_fail_cnt = send_fail_cnt.wrapping_add(1);
                    if send_fail_cnt >= agent_conf.send_fail_limit as u16 {
                        log::error!("Heartbeat limit count ..return err");
                        return Err(Error::SenderError("limit heartbeat count".to_string()))
                    }
                    continue;
                }
                let wret = client.flush().await;
                log::debug!("Send heartbeat data flush return {:?}", wret);
                if wret.is_err() {
                    // send_fail_cnt = send_fail_cnt + 1;
                    send_fail_cnt = send_fail_cnt.wrapping_add(1);
                    if send_fail_cnt >= agent_conf.send_fail_limit as u16 {
                        log::error!("Heartbeat limit count ..return err");
                        return Err(Error::SenderError("limit heartbeat count".to_string()))
                    }
                }
            }
            _ = client.readable() => {
                // TCP流上获取到了数据,解析并处理
                let mut have_data = false;
                // 即便readable()返回代表可读，但读取时仍然可能返回WouldBlock
                match client.try_read(&mut tcp_recv_msg) {
                    Ok(n) => {    // 成功读取了n个字节的数据
                        tcp_recv_msg.truncate(n);
                        if n != 0 {
                            have_data = true;
                        }
                    }
                    Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                        continue;
                    }
                    Err(e) => {
                        log::error!("TCP recv error {:?}\n", e);
                        return Err(Error::Timeout);
                    }
                }
                if !have_data {
                    continue;
                }
                log::info!("GOT Server message dump:\n {:?}\n", tcp_recv_msg.hex_dump());
                // TODO: 解析返回数据
            }
        }
    }
}

pub async fn upload_data_rt(agent_conf: &AgentConf) -> CloudPacket {
    let vin_num = agent_conf.vin_num.as_bytes();
    let vin_num = <[u8; 17]>::try_from(vin_num).unwrap();

    // 整机数据
    let mut v_data = VehicleDataFrame::new();
    v_data.running_state = 0x02;
    v_data.charge_state = 0x01;
    v_data.running_mode = 0x01;
    v_data.speed = 500; // 500表示50km/h
    v_data.record_mileage = 7000; // 7000表示700km
    v_data.total_current = 20;
    v_data.total_voltage = 30;
    v_data.dcdc_state = 0x01;
    let v_p_item_01 = UploadPayloadItem {
        type_id: UploadDataType::Vehicle.into(),
        frame: Some(UploadPayloadItemFrame::VData(v_data.clone())),
    };

    // 驱动电机数据
    let mut dm_data = DriveMotorDataFrame::new();
    dm_data.count = 1;
    dm_data.detail_vec.push(DriveMotorDataFrameItem::new());
    let v_p_item_dm = UploadPayloadItem {
        type_id: UploadDataType::DriveMotor.into(),
        frame: Some(UploadPayloadItemFrame::DriveMotor(dm_data.clone())),
    };

    // 燃料电池数据
    let mut fc_data = FuelCellDataFrame::new();
    fc_data.voltage = 1000;
    fc_data.current = 2000;
    fc_data.consume_rate = 3000;
    fc_data.probe_count = 2;
    fc_data.probe_val_vec.push(50);
    fc_data.probe_val_vec.push(70);
    let v_p_item_fc = UploadPayloadItem {
        type_id: UploadDataType::FuelCell.into(),
        frame: Some(UploadPayloadItemFrame::FuelCell(fc_data.clone())),
    };

    // 定位数据
    let mut l_data = LocationDataFrame {
        loc_state: 0,
        longitude: 0,
        latitude: 0,
    };
    l_data.update_longi(121.506186);
    l_data.update_lati(31.243034);
    let v_p_item_loc = UploadPayloadItem {
        type_id: UploadDataType::Location.into(),
        frame: Some(UploadPayloadItemFrame::Location(l_data.clone())),
    };

    // 极值数据
    let mut extre_data = ExtremeValueDataFrame::new();
    extre_data.highest_temperature_probe_id = 1;
    extre_data.highest_temperature_val = 220;
    extre_data.highest_temperature_subsys_id = 1;
    extre_data.highest_voltage_single_id = 1;
    extre_data.highest_voltage_subsys_id = 1;
    extre_data.battery_single_voltage_highest = 1500;

    extre_data.lowest_temperature_probe_id = 2;
    extre_data.lowest_temperature_val = 120;
    extre_data.lowest_temperature_subsys_id = 1;
    extre_data.lowest_voltage_single_id = 1;
    extre_data.lowest_voltage_subsys_id = 1;
    extre_data.battery_single_voltage_lowest = 900;

    let v_p_item_extre = UploadPayloadItem {
        type_id: UploadDataType::ExtremeValue.into(),
        frame: Some(UploadPayloadItemFrame::ExtremeValue(extre_data.clone())),
    };

    // 发动机数据
    let mut engine_data = EngineDataFrame::new();
    engine_data.state = 0x01;
    engine_data.crankshaft_speed = 6000;
    engine_data.fuel_consume_rate = 7000;
    let v_p_item_engine = UploadPayloadItem {
        type_id: UploadDataType::Engine.into(),
        frame: Some(UploadPayloadItemFrame::Engine(engine_data.clone())),
    };

    // 报警数据
    let mut alarm_data = AlarmDataFrame::new();
    alarm_data.highest_level = 1;
    alarm_data.common_flag = 0;
    let v_p_item_alarm = UploadPayloadItem {
        type_id: UploadDataType::Alarm.into(),
        frame: Some(UploadPayloadItemFrame::Alarm(alarm_data.clone())),
    };

    // 自定义用户数据
    let mut user_data_01 = UserCustomDataFrame::new();
    user_data_01.data_length = 3;
    user_data_01.data_content.push(0x77);
    user_data_01.data_content.push(0x88);
    user_data_01.data_content.push(0x99);
    let v_p_item_user01 = UploadPayloadItem {
        type_id: UploadDataType::UserCustomA.into(),
        frame: Some(UploadPayloadItemFrame::UserCustom(user_data_01.clone())),
    };

    // 拼装payload数据
    let mut v_p_payload = RTDataUploadReqPayload::new();
    v_p_payload.data_collect_time = fill_current_time();
    v_p_payload.append_item(&v_p_item_01);
    v_p_payload.append_item(&v_p_item_dm);
    v_p_payload.append_item(&v_p_item_fc);
    v_p_payload.append_item(&v_p_item_engine);
    v_p_payload.append_item(&v_p_item_loc);
    v_p_payload.append_item(&v_p_item_extre);
    v_p_payload.append_item(&v_p_item_alarm);
    v_p_payload.append_item(&v_p_item_user01);


    // 拼装packet

    let p_header = ProtoCloudHeader {
        f_start: 0x2323,
        cmd_id: CloudCmdType::RealTimeInfoUpload as u8,
        resp_flag: ResponseType::Command as u8,
        vin_id: vin_num,
        encrypt_type: EncryptionType::None as u8,
        payload_len: 0,
    };

    let p_pkt = CloudPacket {
        header: p_header,
        payload: Some(CPPayload::UploadRTDataReq(v_p_payload)),
        crc: 0,
        retry_cnt: 0,
    };

    // let mut p_header = ProtoCloudHeader::new();
    // p_header.resp_flag = 0;
    // p_header.cmd_id = CloudCmdType::RealTimeInfoUpload.into();
    // let mut p_pkt = CloudPacket::new();
    // p_pkt.header = p_header;
    // p_pkt.set_payload(Some(CPPayload::UploadRTDataReq(v_p_payload)));
    p_pkt
}

pub async fn send_heartbeat(msg_sender: Sender<CloudPacket>, agent_conf: &AgentConf) {
    let hb_pkt = gen_heartbeat_req(agent_conf.vin_num.as_ref());
    if hb_pkt.is_err() {
        log::error!("Generate heartbeat req failed {:?}", hb_pkt.unwrap_err());
        return;
    }
    // TODO: 返回错误应该如何处理
    let ret = msg_sender.send(hb_pkt.unwrap()).await;
    if ret.is_err() {
        log::error!("Send heartbeart to channel failed -> {:?}", ret.unwrap_err());
    }
}

// pub async fn send_data_to_cache(cache_type: CacheType, msg_cache: &mut MsgCache, agent_conf: &AgentConf, p_pkt: CloudPacket) {
//     let ret = msg_cache.write_pkt(cache_type, p_pkt).await;
//     if ret.is_err() {
//         // TODO: 写入失败应该处理
//         log::error!("Write data to realtime channel failed -> {:?}", ret.unwrap_err());
//     }
// }

pub async fn send_data_to_retry(retry_time: u64, msg_cache: &mut MsgCache, pkt: CloudPacket) {
    let clone_tx = msg_cache.msg_retry_tx.clone();
    tokio::spawn(async move {
        log::info!("Sleeping {:?} secs for waiting send retry packet, retry_cnt:{:?}", retry_time, pkt.retry_cnt);
        time::sleep(time::Duration::from_secs(retry_time)).await;
        let ret = clone_tx.send(pkt).await;
        // if ret.is_err() {
        //     // TODO: 写入失败应该处理
        //     log::error!("Write data to realtime channel failed -> {:?}", ret.unwrap_err());
        // }
        log::info!("Send retry packet to queue ret {:?}", ret);
    });
}